package com.wngbms.async;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

@Slf4j
public class CompletableDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        m1();
//        m2();
//        m3();
//        m4();
//        whenComplete();
//        handle();
//        thenCombine();
//        thenAcceptBoth();
//        runAfterBoth();
//        applyToEither();
//        acceptEither();
//        runAfterEither();
//        thenCompose();

        complete();

    }

    /**
     * 提前返回
     *
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void complete() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            sleep(3000);
            log.info("do task1 end");
            return 1.2;
        });

        CompletableFuture<Double> cf2 = cf.whenComplete((a, b) -> {
            log.info("do task2");
            sleep(2000);
            log.info("do task2 end");
        });

        cf.complete(999.0);
        System.out.println(cf2.get());
    }

    private static void thenCompose() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            sleep(3000);
            log.info("do task1 end");
            return 1.2;
        });

//        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
//            log.info("do task2");
//            sleep(2000);
//            log.info("do task2 end");
//            return "success4";
//        });

        CompletableFuture<String> cf3 = cf.thenCompose(r -> {
            log.info("do task3");
            sleep(2000);
            log.info("do task3 end");
            // return cf2;
            return CompletableFuture.supplyAsync(() -> {
                log.info("do task2");
                sleep(2000);
                log.info("do task2 end");
                return "success4";
            });
        });

        System.out.println(cf3.get());
    }

    /**
     * 无入参, 无返回值, 块的先完成
     *
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void runAfterEither() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            sleep(3000);
            log.info("do task1 end");
            return 1.2;
        });

        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
            log.info("do task2");
            sleep(2000);
            log.info("do task2 end");
            return 4.5;
        });

        CompletableFuture<Void> cf3 = cf.runAfterEither(cf2, () -> {
            log.info("do all ");
            sleep(2000);
            log.info("do all end");
        });

        System.out.println(cf.get());
        System.out.println(cf2.get());
        System.out.println(cf3.get());
    }

    /**
     * 有入参, 无返回值, 块的先完成
     *
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void acceptEither() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            sleep(3000);
            log.info("do task1 end");
            return 1.2;
        });

        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
            log.info("do task2");
            sleep(2000);
            log.info("do task2 end");
            return 4.5;
        });

        CompletableFuture<Void> cf3 = cf.acceptEither(cf2, r -> {
            log.info("do all {} ", r);
            sleep(2000);
            log.info("do all end");
        });

        System.out.println(cf.get());
        System.out.println(cf2.get());
        System.out.println(cf3.get());
    }

    /**
     * 有入参, 有返回值, 快的先完成
     *
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void applyToEither() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            sleep(1000);
            log.info("do task1 end");
            return 1.2;
        });

        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
            log.info("do task2");
            sleep(2000);
            log.info("do task2 end");
            return 4.5;
        });

        CompletableFuture<String> cf3 = cf.applyToEither(cf2, r -> {
            log.info("do all {} ", r);
            sleep(2000);
            log.info("do all end");
            return "success";
        });

        System.out.println(cf.get());
        System.out.println(cf2.get());
        System.out.println(cf3.get());
    }

    /**
     * 无入参, 无返回值, 两个都完成
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    private static void runAfterBoth() throws ExecutionException, InterruptedException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            sleep(2000);
            log.info("do task1 end");
            return 1.2;
        });

        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
            log.info("do task2");
            sleep(2000);
            log.info("do task2 end");
            return 4.5;
        });

        // 无入参, 无返回值
        CompletableFuture<Void> cf3 = cf.runAfterBoth(cf2, () -> {
            log.info("do task5");
            sleep(2000);
            log.info("do task5 end");
        });

        System.out.println(cf.get());
        System.out.println(cf2.get());
        System.out.println(cf3.get());
    }

    /**
     * 有入参, 无返回值, 两个都完成
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    private static void thenAcceptBoth() throws ExecutionException, InterruptedException {

        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            sleep(2000);
            log.info("do task1 end");
            return 1.2;
        });

        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
            log.info("do task2");
            sleep(2000);
            log.info("do task2 end");
            return 4.5;
        });

        // cf1, cf2 执行完(cf1, cf2 都有返回值), 将结果传入, 无返回值
        CompletableFuture<Void> cf3 = cf.thenAcceptBoth(cf2, (a, b) -> {
            log.info("do task4");
            sleep(2000);
            log.info("do task4 end");
        });

        System.out.println(cf.get());
        System.out.println(cf2.get());
        System.out.println(cf3.get());
    }

    /**
     * 有入参, 有返回值, 两个都完成
     *
     * @throws ExecutionException
     * @throws InterruptedException
     */
    private static void thenCombine() throws ExecutionException, InterruptedException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            sleep(2000);
            log.info("do task1 end");
            return 1.2;
        });

        CompletableFuture<Double> cf2 = CompletableFuture.supplyAsync(() -> {
            log.info("do task2");
            sleep(2000);
            log.info("do task2 end");
            return 4.5;
        });

        // cf1 cf2 执行完(cf1, cf2 都有返回值), 将结果传入, 且有返回值
        CompletableFuture<String> cf3 = cf.thenCombine(cf2, (a, b) -> {
            log.info("do all {} {}", a, b);
            sleep(2000);
            log.info("do all {} {} end", a, b);
            return "success";
        });

        System.out.println(cf.get());
        System.out.println(cf2.get());
        System.out.println(cf3.get());
    }

    public static void sleep(long l) {
        try {
            Thread.sleep(l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * handle 方法有返回值
     *
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void handle() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            return 1.2;
        });

        CompletableFuture<String> cf2 = cf.handle((a, b) -> {
            log.info("do task2 {} {}", a, b);
            return "success";
        });

        System.out.println(cf2.get());
    }

    /**
     * whenComplete 无返回值
     *
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void whenComplete() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");

            if (true) {
                throw new RuntimeException("exp");
            }

            return 1.2;
        });

        CompletableFuture<Double> cf2 = cf.whenComplete((a, b) -> {
            // 非异常场景 b为null
            log.info("do task2 {} {}", a, b);
        });

        System.out.println(cf2.get());
        System.out.println(cf.get());
    }

    private static void m4() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");

            if (true) {
                throw new RuntimeException("exp");
            } else {
                return 1.3;
            }
        });

        CompletableFuture<Double> cf2 = cf.exceptionally(p -> {
            log.error("do exp", p);
            return -1.0;
        });

        CompletableFuture<Void> cf3 = cf.thenAccept(p -> {
            log.info("do task2 {}", p);
        });

        System.out.println(cf2.get());
        System.out.println(cf3.get());
        System.out.println(cf.get());
    }

    private static void m3() throws InterruptedException, ExecutionException {
        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");
            return 1.2;
        });

        CompletableFuture<Void> cf2 = cf.thenApply(r -> {
            log.info("do task2");
            return "t2\t" + r;
        }).thenAccept(r -> {
            log.info("do task3");
        }).thenRun(() -> {
            log.info("do task4");
        });

        System.out.println(cf.get());
        System.out.println(cf2.get());
    }

    private static void m2() throws InterruptedException, ExecutionException {
//        CompletableFuture<Void> c = CompletableFuture.runAsync()

        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CompletableFuture<Double> cf = CompletableFuture.supplyAsync(() -> {
            log.info("do task1");

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (false) {
                throw new RuntimeException("exp");
            } else {
                return 1.3;
            }
        }, executorService);

        // 如果执行了 cf.get(), task2 是由main线程来执行
        // 否则 task2 是由 task1 的线程来执行
//        System.out.println(cf.get());

        // 某个任务执行完, 回调方法
        CompletableFuture<String> cf2 = cf.thenApply(r -> {
            log.info("do task2");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return r + "\thhh";
        });

//        System.out.println(cf2.get());

        // Async版本 task3 不是由 执行 task2 的线程执行的
        CompletableFuture<String> cf3 = cf2.thenApplyAsync(r -> {
            log.info("do task3");

            return r + "\t yyy";
        }, executorService);

        System.out.println(cf.get());
        System.out.println(cf2.get());
        System.out.println(cf3.get());
    }

    private static void m1() throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newFixedThreadPool(19);
        Future<Double> exp = executorService.submit(() -> {
            Thread.sleep(30000);

            if (false) {
                throw new RuntimeException("exp");
            } else {
                return 1.3;
            }
        });
        System.out.println(exp.get());
    }

}
